﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace Snake
{
    class GameLogic
    {
        private readonly GameField field;
        private int speed = 500;
        private int roundSpeed = 500;
        private int roundNumber = 1;
        private List<ConsoleNode> playerNodes;
        private readonly Painter paint;
        private readonly KeyboardHendler kh;
        private ConsoleNode target;
        private int nodesCount = 1;
        private readonly Random rnd = new Random();

        public GameLogic(GameField gameField, Painter painter, ConsoleNode playerNode, KeyboardHendler handler)
        {
            field = gameField;
            paint = painter;
            playerNodes = new List<ConsoleNode>();
            playerNodes.Add(playerNode);
            kh = handler;
            kh.KeyLeft += Left;
            kh.KeyUp += Up;
            kh.KeyRight += Right;
            kh.KeyDown += Down;
            kh.Pause += Pause;
        }

        public void PlayGame()
        {
            CreateTarget();
            CreateSnake(playerNodes[0]);
            Console.WriteLine("For play, push any ArrowKey, for pause -- Spacebar");
            for (; ;)
            {
                paint.DraweGameFieldFrame(field);
                paint.DrawNodes(target);
                paint.DrawNodeList(playerNodes);
                paint.DrawScore(field, 5, nodesCount - 1);
                kh.QueryKB();
                if (playerNodes[0].move != Direction.Pause)
                {
                    MoveTailOfSnake(playerNodes);
                }
                
                playerNodes[0].Move(CheckPosition);
                
                if (IsOnTarget(playerNodes[0]))
                {
                    playerNodes.Add(new ConsoleNode(playerNodes[nodesCount].X, playerNodes[nodesCount].Y,
                playerNodes[0].ColorNode, playerNodes[0].GameNodeSymbol));
                    GeneraterandomCoordinates(target);
                    nodesCount++;
                }
                if (!IsOnField(playerNodes[0].X, playerNodes[0].Y) || !CrossTail())
                {
                    Console.SetCursorPosition(field.StartX + field.Width + 1 + 5, field.StartY + 5);
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("GAME OVER");
                    break;
                }
                ChangeSpeedSnake();
                Thread.Sleep(speed);
            }
        }

        private int ChangeSpeedSnake()
        {
            if (playerNodes.Count > 5 && playerNodes.Count < 11)
                speed = 400;
            if (playerNodes.Count > 10 && playerNodes.Count < 16)
                speed = 300;
            if (playerNodes.Count > 15 && playerNodes.Count < 21)
                speed = 250;
            if (playerNodes.Count > 20 && playerNodes.Count < 26)
                speed = 200;
            if (playerNodes.Count > 25 && playerNodes.Count < 31)
                speed = 150;
            if (playerNodes.Count > 30)
                speed = 100;
            return speed;
        }

        private void CreateSnake(ConsoleNode node)
        {
            bool check = false;
            while (!check)
            {
                GeneraterandomCoordinates(playerNodes[0]);
                if (IsOnField(playerNodes[0].X, playerNodes[0].Y + 1) 
                    && IsOnField(playerNodes[0].X, playerNodes[0].Y + 2))
                {
                    playerNodes.Add(new ConsoleNode(playerNodes[0].X, playerNodes[0].Y + 1,
                        playerNodes[0].ColorNode, playerNodes[0].GameNodeSymbol));
                    playerNodes.Add(new ConsoleNode(playerNodes[0].X, playerNodes[0].Y + 2,
                        playerNodes[0].ColorNode, playerNodes[0].GameNodeSymbol));
                    check = true;
                }
            } 
        }

        private void MoveTailOfSnake(List<ConsoleNode> nodes)
        {
            for (int i = playerNodes.Count - 1; i >= 1; i--)
            {
                    playerNodes[i].X = playerNodes[i - 1].X;
                    playerNodes[i].Y = playerNodes[i - 1].Y;
            }
        }

        private bool IsOnTarget(ConsoleNode node)
        {
            if (node.X == target.X && node.Y == target.Y)
                return true;
            return false;
        }

        private ConsoleNode GeneraterandomCoordinates(ConsoleNode node)
        {
            bool check = false;
            while (!check)
            {
                int X = rnd.Next(field.StartX + 1, field.Width + field.StartX);
                int Y = rnd.Next(field.StartY + 1, field.Height + field.StartY);
                if (CrossNodes(X, Y))
                {
                    node.X = X;
                    node.Y = Y;
                    check = true;
                }
            }
            return node;
        }

        private void CreateTarget()
        {
            bool check = false;
            while (!check)
            {
                int X = rnd.Next(field.StartX + 1, field.Width + field.StartX);
                int Y = rnd.Next(field.StartY + 1, field.Height + field.StartY);
                if (X != playerNodes[0].X && Y != playerNodes[0].Y)
                {
                    target = new ConsoleNode(X, Y, ConsoleColor.White, 'X');
                    check = true;
                }
            }
        }

        private bool IsOnField(int x, int y)
        {
            if ((x >= field.StartX) && (x < field.Width + field.StartX)
                && (y >= field.StartY) && (y < field.Height + field.StartY))
                return true;
            return false;
        }

        private Position CheckPosition(int x, int y)
        {
            if (!CrossNodes(x, y))
                return Position.Node;
            if (!IsOnField(x, y))
                return Position.Border;
            return Position.Free;
        }

        public bool CrossTail()
        {
            for (int i = 2; i < playerNodes.Count; i++)
            {
                if (playerNodes[i].X == playerNodes[0].X && playerNodes[i].Y == playerNodes[0].Y)
                    return false;
            }
            return true;
        }

        private bool CrossNodes(int x, int y)
        {
            foreach (var node in playerNodes)
            {
                if (node.X == x && node.Y == y)
                    return false;
            }
            return true;
        }

        private void Left(object sender, EventArgs e)
        {
            if (CheckPosition(playerNodes[0].X - 1, playerNodes[0].Y) == Position.Free 
                && IsOnField(playerNodes[0].X - 1, playerNodes[0].Y)  && playerNodes[0].move != Direction.Right)
                playerNodes[0].move = Direction.Left;
        }
        private void Right(object sender, EventArgs e)
        {
            if (CheckPosition(playerNodes[0].X + 1, playerNodes[0].Y) == Position.Free 
                && IsOnField(playerNodes[0].X + 1, playerNodes[0].Y) && playerNodes[0].move != Direction.Left)
                playerNodes[0].move = Direction.Right;
        }
        private void Up(object sender, EventArgs e)
        {
            if (CheckPosition(playerNodes[0].X, playerNodes[0].Y - 1) == Position.Free 
                && IsOnField(playerNodes[0].X, playerNodes[0].Y - 1) && playerNodes[0].move != Direction.Down)
                playerNodes[0].move = Direction.Up;
        }
        private void Down(object sender, EventArgs e)
        {
            if (CheckPosition(playerNodes[0].X, playerNodes[0].Y + 1) == Position.Free 
                && IsOnField(playerNodes[0].X, playerNodes[0].Y + 1) && playerNodes[0].move != Direction.Up)
                playerNodes[0].move = Direction.Down;
        }

        private void Pause(object sender, EventArgs e)
        {
            playerNodes[0].move = Direction.Pause;
        }

        public void StartGame()
        {
            int round = 1;
            int score = 0;
            CreateSnake(playerNodes[0]);
            bool gameOver = false;
            do
            {
                Console.Clear();
                CreateTarget();
                Console.WriteLine("For play, push any ArrowKey, for pause -- Spacebar");

                for (;;)
                {
                    paint.DraweGameFieldFrame(field);
                    paint.DrawNodes(target);
                    paint.DrawNodeList(playerNodes);
                    paint.DrawScore(field, 5, score, round);
                    kh.QueryKB();
                    if (playerNodes[0].move != Direction.Pause)
                    {
                        MoveTailOfSnake(playerNodes);
                    }

                    playerNodes[0].Move(CheckPosition);

                    if (IsOnTarget(playerNodes[0]))
                    {
                        playerNodes.Add(new ConsoleNode(playerNodes[nodesCount].X, playerNodes[nodesCount].Y,
                    playerNodes[0].ColorNode, playerNodes[0].GameNodeSymbol));
                        GeneraterandomCoordinates(target);
                        score += roundNumber;
                    }
                    if (!IsOnField(playerNodes[0].X, playerNodes[0].Y) || !CrossTail())
                    {
                        Console.SetCursorPosition(field.StartX + field.Width + 1 + 5, field.StartY + 5);
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("GAME OVER");
                        gameOver = true;
                        break;
                    }
                    if (ReadyForNewRound(playerNodes.Count))
                    {
                        playerNodes.RemoveRange(1, playerNodes.Count - 1);
                        CreateSnake(playerNodes[0]);
                        nodesCount = 1;
                        round++;
                        playerNodes[0].move = Direction.Pause;
                        break;
                    }

                Thread.Sleep(roundSpeed);
                }

            }
            while (!gameOver);



        }

        private bool ReadyForNewRound(int countNodes)
        {
            if (countNodes > 10 && roundNumber == 1)
            {
                roundNumber = 2;
                roundSpeed = 400;
                return true;
            }
            else if (countNodes > 15 && roundNumber == 2)
                {
                    roundNumber = 3;
                    roundSpeed = 300;
                    return true;
                }
                else if (countNodes > 20 && roundNumber == 3)
                {
                    roundNumber = 4;
                    roundSpeed = 250;
                    return true;
                }
                    else if (countNodes > 25 && roundNumber == 4)
                    {
                        roundNumber = 5;
                        roundSpeed = 200;
                        return true;
                    }
                        else if (countNodes > 28 && roundNumber == 5)
                        {
                            roundNumber = 6;
                            roundSpeed = 150;
                            return true;
                        }
                            else if (countNodes > 35)
                            {
                                roundSpeed = 100;
                                return true;
                            }
            return false;
        }

    }

    enum Position
    {
        Node,
        Border,
        Free
    }
}
